package me.xiaoyan.android.tuition.utils;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * RSA加密类
 * 
 * @author 马俊
 * 
 */
public class RSAUtil {

	/**
	 * 密钥长度
	 */
	public static final int KEY_SIZE = 1024;
	
	private static Provider provider = new BouncyCastleProvider();
	
	private static KeyFactory keyFac = null;
	static{
		try {
			keyFac = KeyFactory.getInstance("RSA", provider);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 生成密钥对
	 * 
	 * @return
	 * @throws Exception
	 */
	public static KeyPair generateKeyPair() throws Exception {
		try {
			KeyPairGenerator keyPairGen = (KeyPairGenerator) KeyPairGenerator.getInstance("RSA",provider);
			keyPairGen.initialize(KEY_SIZE, new SecureRandom());
			KeyPair keyPair = keyPairGen.genKeyPair();
			return keyPair;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 生成公钥
	 * 
	 * @param mod
	 * @return
	 * @throws Exception
	 */
	public static RSAPublicKey createRSAPublicKeyByModulus(String mod,
			String pubEx) throws Exception {
		RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(mod),
				new BigInteger(pubEx));

		return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
	}

	/**
	 * 生成私钥
	 * 
	 * @param mod
	 * @param priEx
	 * @return
	 * @throws Exception
	 */
	public static RSAPrivateKey createRSAPrivateKeyByModulus(String mod,
			String priEx) throws Exception {
		RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(
				new BigInteger(mod), new BigInteger(priEx));
		return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
	}

	/**
	 * 加密数据
	 * 
	 * @param key
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encrypt(Key key, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",provider);
			cipher.init(Cipher.ENCRYPT_MODE, key);
			int blockSize = cipher.getBlockSize();
			int outputSize = cipher.getOutputSize(data.length);
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
					: data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i
							* outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i
							* blockSize, raw, i * outputSize);
				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 解密数据
	 * 
	 * @param key
	 * @param raw
	 * @return
	 * @throws Exception
	 */
	public static byte[] decrypt(Key key, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",provider);
			cipher.init(Cipher.DECRYPT_MODE, key);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;
			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
	
	/**
	 * 使用公钥进行加密
	 * @param RSApubKey 	加密的密钥
	 * @param plainText 	待加密的明文数据
	 * @return 				加密后的数据
	 * @throws Exception
	 */
	public static byte[] RSAEncrypt(String plainText, CipherParameters RSApubKey)
			throws Exception {
		if (plainText == null || plainText.length() == 0) {
			throw new Exception("plainText is null");
		} else if (RSApubKey == null) {
			throw new Exception("publicKey is null");
		}
		byte[] rv = null;
		AsymmetricBlockCipher eng = new RSAEngine();
		eng = new PKCS1Encoding(eng); // PKCS1填充

		eng.init(true, RSApubKey); // true 加密 公钥 false 解密

		byte[] ptBytes = plainText.getBytes();
		rv = eng.processBlock(ptBytes, 0, ptBytes.length);
		return rv;

	} // RSAEncrypt end
	
	/**
	 * 使用私钥解密
	 * @param RSAprivKey	解密的密钥
	 * @param cipherText	已经加密的数据
	 * @return 				解密后的明文
	 * @throws Exception
	 */
	public static byte[] RSADecrypt(byte[] cipherText,
			CipherParameters RSAprivKey) throws Exception {
		if (cipherText == null || cipherText.length == 0) {
			// 输入null
			return null;
		}
		byte[] rv = null;
		AsymmetricBlockCipher eng = new RSAEngine();
		eng = new PKCS1Encoding(eng); // PKCS1填充
		eng.init(false, RSAprivKey); // 进行解密操作
		rv = eng.processBlock(cipherText, 0, cipherText.length);
		return rv;

	}// RSADecrypt end
	
}
